import defaultsDeep from 'lodash-es/defaultsDeep';
import forEach from 'lodash-es/forEach';
import node from './node';
import G6 from './lib/_g6';
import * as d3 from 'd3';

class Topo {
    constructor(option) {
        this._option = option;
        this.dom = option.dom;
        this.optionStyle = option.optionStyle;
        this.automaticLayout = option.automaticLayout;
        this.tension = option.tension;
        this.defaultEdge = option.defaultEdge;
        this.defaultNode = option.defaultNode;
        this.data = option.data || {
            nodes: [],
            edges: []
        };
        this.config = defaultsDeep(option.config || {}, Topo.defaultOptions);
        this.events = option.events || {};
        this.init();
    }
    /**
     * 初始化
     */
    init() {
        let {dom = null,optionStyle = {}} = this;
        this.graph = new G6.Graph({
            container: dom,
            width: optionStyle.width,
            height: optionStyle.height,
            fitView: 'autoZoom',
            maxZoom:1.1,
            autoPaint:true,
            modes: {
                default: ['hehaviourName']
            },
        });
        let node,dx,dy;
        this.graph.on('node:drag', ev=>{
            node && this.graph.update(node, {
                x: ev.x+dx,
                y: ev.y+dy
            });
        });
        this.graph.on('node:dragstart', ev=>{
            const {item} = ev;
            const model = item.getModel();
            node = item;
            dx = model.x - ev.x;
            dy = model.y - ev.y;
        });
        this.graph.on('node:dragend', ev=>{
            node = undefined;
        });

        //事件管理
        this.handleEvents();
    }

    //D3布局
    layout_D3(_node,cb){
        // 初始化力导布局
        let {optionStyle = {}} = this;
        this.simulation = d3.forceSimulation()
            .force('charge', d3.forceManyBody().strength(()=> this.tension?-70:-1500))
            .force('link', d3.forceLink().id(function(d){return d.index;}).distance(20).strength(1))
            .force('center', d3.forceCenter(optionStyle.width/2,optionStyle.height/2));
        //动态生成坐标
        let nodes = _node.filter(item => item.x);
        let _nodes = _node.filter(item => !item.x);
        this.simulation.nodes(_nodes)
            .on('tick', ()=>cb(nodes.concat(_nodes)));
    }

    /**
     * 数据更新
     */
    update = (ev)=>{
        let record = true;
        let {defaultNode = {}, defaultEdge = {}} = this;
        let {nodes = [], edges = []} = ev || {};
        let _paintings = (e)=>{
            let _data = {
                nodes:e.map(item=>{
                    return {
                        ...defaultNode,
                        ...node[item.type],
                        ...item
                    };
                }),
                edges:edges.map(item=>{
                    return {
                        ...item,
                        ...defaultEdge,
                        shape:item.source==item.target?'sinceRing':defaultEdge.shape
                    };
                })
            };
            this.graph.read(_data);
        };
        if(this.automaticLayout){
            this.layout_D3(nodes,(e)=>{
                if (this.tension || record) {
                    _paintings(e);
                    record = false;
                }
            });
        }else{
            _paintings(nodes);
        }
    }

    /**
     * 事件管理
     */
    handleEvents() {
        forEach(this.events, (fn, name) => {
            this.graph.on(name, function () {
                fn.apply(this, arguments);
            });
        });
        return this;
    }
    
}

export default Topo;